package com.demo.dubborpc.netty;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.util.concurrent.Callable;

/**
 * @author gy
 * @date 2022/8/7
 */
public class NettyClientHandler extends ChannelInboundHandlerAdapter implements Callable {
    /**
     * 在通道建立连接时，将上下文的信息保存了
     */
    private ChannelHandlerContext context;

    /**
     * 服务器端的响应信息
     */
    private String response;

    /**
     * 发送给服务器端的报文，传入的参数
     */
    private String request;

    /**
     * 执行顺序（1）
     * 在通道建立的时候就会激活
     * 将 ChannelHandlerContext 存储起来，其他方法要用到
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive 被调用");
        context = ctx;
    }

    /**
     * 执行顺序（4）
     * 收到服务器的响应后，调用该方法
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("channelRead 被调用");
        response = msg.toString();
        // 唤醒等待的线程
        notify();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }

    /**
     * 执行顺序（3）-> 被唤醒后继续执行 执行顺序（5）
     * 被代理对象调用，发送请求给服务器 -> wait() -> 等待被唤醒（channelRead）-> 返回结果
     * @return
     * @throws Exception
     */
    @Override
    public synchronized Object call() throws Exception {
        System.out.println("call1 被调用(唤醒前)");
        context.writeAndFlush(request);
        // 进行wait（） 等待被唤醒，由于netty 返回的消息会经过 channelRead方法，所以在channelRead方法中被唤醒
        // channelRead 和 call 两者存在同步的关系，故添加 synchronized 关键字
        wait();
        System.out.println("call2 被调用(唤醒后)");
        // 服务方返回的响应结果
        return response;
    }

    /**
     * 执行顺序（2）
     * 代理对象 给 请求体 赋值
     * @param request
     */
    public void setRequest(String request) {
        System.out.println("setRequest 被调用 ");
        this.request = request;
    }
}
